package il.ac.tau.cvws.scrabblesidekick.client.engine;

import il.ac.tau.cvws.scrabblesidekick.client.app.AppServices;
import il.ac.tau.cvws.scrabblesidekick.client.app.controller.CameraController;
import il.ac.tau.cvws.scrabblesidekick.client.app.controller.GameController;
import il.ac.tau.cvws.scrabblesidekick.client.engine.storage.DataKeyStorage;
import il.ac.tau.cvws.scrabblesidekick.client.ui.constant.UserMessageConstant;
import il.ac.tau.cvws.scrabblesidekick.client.util.communication.CommCallback;
import il.ac.tau.cvws.scrabblesidekick.shared.comm.request.FindWordRequestData;
import il.ac.tau.cvws.scrabblesidekick.shared.comm.response.ExceptionResponsePayload;
import il.ac.tau.cvws.scrabblesidekick.shared.comm.response.FindWordResponsePayload;
import il.ac.tau.cvws.scrabblesidekick.shared.comm.response.ResponsePayload;
import il.ac.tau.cvws.scrabblesidekick.shared.constant.CommConstants;
import il.ac.tau.cvws.scrabblesidekick.shared.datatype.ImageData;
import il.ac.tau.cvws.scrabblesidekick.shared.datatype.StorageDataKey;
import il.ac.tau.cvws.scrabblesidekick.shared.datatype.WordPlacement;
import il.ac.tau.cvws.scrabblesidekick.shared.datatype.game.Rack;
import il.ac.tau.cvws.scrabblesidekick.shared.datatype.game.Tile;

import java.util.ArrayList;
import java.util.List;

/* The application's game engine. The game engine is responsible for orchestrating
 * the flow of the game; this includes asking the user to enter their rack letters,
 * and then taking a picture of the game board, finally sending all the gathered
 * information to the server for processing.
 */
public class GameEngine extends CameraEngine
{
	private final FindWordRequestData _gameData;
	
	public GameEngine(CameraController controller)
	{
		super(controller);
		
		_gameData = new FindWordRequestData();
	}
	
	/* Indicate to the engine that a game request flow is starting.
	 */
	public boolean startGame()
	{
		StorageDataKey key = null;
		
		try
		{
			/* Try to retrieve the saved application key.
			 */
			key = DataKeyStorage.retrieveDataKey();
		}
		catch (Exception e) {}
		
		/* If the key wasn't retrieved, it is because either the application
		 * hadn't been calibrated yet, or there was some problem or corruption
		 * with the key storage; in that case, return false to let the user
		 * know about the problem and (re)calibrate the device.
		 */
		if (key == null)
		{
			return false;
		}
		
		_gameData.setStorageKey(key);
		
		return true;
	}
	
	/* Set the device screen dimensions.
	 */
	public void handleScreen(int screenWidth, int screenHeight)
	{
		_gameData.setScreenWidth(screenWidth);
		_gameData.setScreenHeight(screenHeight);
	}
	
	/* Set the request's encoded board image.
	 */
	public void handleBoardImage(ImageData boardImage)
	{
		_gameData.setBoardImage(boardImage);
	}
	
	/* Set the user's entered rack letters.
	 */
	public void handleRack(char[] rack)
	{
		List<Tile> tiles = new ArrayList<Tile>();
		
		for (char c : rack)
		{
			/* The rack letters get translated from characters to dictionary indices.
			 * The value of the tile is set to 1 one at this stage since it is
			 * irrelevant to the request.
			 */
			tiles.add(new Tile(AppServices.instance().dictionary().getIndex(c), 1));
		}
		
		_gameData.setRack(new Rack(tiles)); 
	}
	
	/* Serialize and send the request data to the server for solving.
	 */
	public void solveGame()
	{
		try
		{
			AppServices.instance().comm().sendPostRequest(
					CommConstants.SERVER_URL + CommConstants.GAME_SERVLET, 
					CommConstants.PAYLOAD, 
					_gameData,
					new CommCallback()
					{
						@Override
						public void onSuccess(ResponsePayload response)
						{
							if (response instanceof ExceptionResponsePayload)
							{
								/* The game solving algorithm failed because of either an exception or
								 * an applicative error.
								 */
								_controller.handleFailure(((ExceptionResponsePayload)response).getMSG());
							}
							else
							{
								/* A solution was found!
								 * Get the solution from the response and send it to the controller
								 * for handling.
								 */
								FindWordResponsePayload gameResponse = (FindWordResponsePayload)response;
								WordPlacement placement = gameResponse.getWord();
								
								((GameController)_controller).handleGameSuccess(placement);	
							}
						}
						
						@Override
						public void onFailure()
						{
							/* The request failed because of a communication error.
							 */
							_controller.handleFailure(UserMessageConstant.RESPONSE_FAILED);	
						}
					});	
		}
		catch (Exception e)
		{
			e.printStackTrace();
			
			/* For any other unexpected error (probably RuntimeExceptions), return a
			 * general failure error response.
			 */
			_controller.handleFailure(UserMessageConstant.RESPONSE_FAILED);
		}
		finally
		{
			/* Once the response is received, try to clear as much memory as possible, so
			 * since we're not yet done with the activity and more images and graphics are
			 * pending.
			 */
			System.gc();
		}
	}
}
